package com.foreveross.pushsdk.main;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;

import com.foreveross.pushsdk.core.HeartbeatReciverList;
import com.foreveross.pushsdk.core.HttpConnection;
import com.foreveross.pushsdk.core.IMessageReceiver;
import com.foreveross.pushsdk.core.PackageUtil;
import com.foreveross.pushsdk.core.PushConstants;
import com.foreveross.pushsdk.core.PushPreferences;
import com.foreveross.pushsdk.core.PushSocket;
import com.foreveross.pushsdk.core.UniqueIdMaker;
import com.foreveross.pushsdk.entity.AuthMessageEntity;
import com.foreveross.pushsdk.entity.Tags;
import com.foreveross.pushsdk.entity.TimeEntity;

import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
/**
 * Socket通信类，用于接收推送消息和发送心跳
 * @author Huang Li
 * @version 创建时间：2014年5月8日 下午4:15:55
 */
public class PushManager  {

    private String server_host;
    private int server_port;
    private String unique;
    private String secret;
    private String appId;
    /**
     * 标签
     */
    private Tags[] tagses;
    private PushSocket pushSocket;

    private Context context;

    /**
     * 推送的令牌
     */
    private String push_token;

    /**
     * token在服务器完成注册的时间
     */
    private String tokenCreateDate;
    /**
     * token在服务器的有效期时间(天)
     */
    private int expiredTime;
    private PushManager self;

    private ConnectTask connectTask;

    /**
     * 是否在Socket服务器对Token进行了认证
     */
    private boolean isSocketAuth;

    /**
     * 当前尝试重新连接的次数，只有在能收到心跳回执或者断网后才能重置为0
     */
    private int currentTryTime=0;
    private static PushManager manager=new PushManager();

    public IMessageReceiver messageReceiver;
    /**
     * 发送心跳的广播接收器
     */
    private BroadcastReceiver receiver=new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(PushConfig.LOG_TAG, "Receiver Heartbeart Broadcast");
            sendHeartBeat();
        }

    };

    private String currentUserId;

    public PushManager(){

    }
    public static PushManager getInstance() {
        return manager;
    }
    public boolean isInited(){
        if (this.context==null) {
            return false;
        } else {
            return true;
        }
    }
    public void init(Context context,String userId,String serverHost,
                     int serverPort,IMessageReceiver msgReceiver,
                     Tags[] tagses,String  secret,String appId) {
        // TODO Auto-generated constructor stub
        this.context=context;
        this.server_host = serverHost;
        this.server_port = serverPort;
        this.messageReceiver=msgReceiver;
        this.secret=secret;
        this.tagses=tagses;
        this.appId=appId;
        IntentFilter iFilter=new IntentFilter(PushConstants.SEND_HEARTBEAT);
        context.registerReceiver(receiver, iFilter);

        currentUserId=userId;
        PushPreferences preferences=new PushPreferences(context,userId);
        this.self=this;
        currentTryTime=0;
        pushSocket=null;

        isSocketAuth=false;
        push_token=preferences.getToken();
        tokenCreateDate=preferences.getTokenCreateTime();
        expiredTime=preferences.getExpiedTime();
        connectTask=new ConnectTask();
        unique=UniqueIdMaker.getUnique(context);
    }
    /**
     * 连接服务器
     */
    public void connect(){
        if(server_host.equals("")||server_port==0){
            Log.e(PushConfig.LOG_TAG,"Push Server Host or Port is Enpty!");
            return ;
        }
        if(currentTryTime>PushConfig.MAX_RETRY_TIMES) {
            stopConnect();
            return;
        }
        if(pushSocket!=null&&pushSocket.isSocketAvailable()){
            Log.i(PushConfig.LOG_TAG, "Socket connect is existed");

        }else {
            if(connectTask.getStatus()!=AsyncTask.Status.RUNNING){
                if(connectTask.getStatus()==AsyncTask.Status.FINISHED) connectTask=new ConnectTask();
                try {
                    connectTask.execute();
                } catch (Exception e) {
                }
            }else {
                Log.i(PushConfig.LOG_TAG, "Connect task Running....");
            }

        }
    }

    private class ConnectTask extends AsyncTask<Void,Void,Void>{

        @Override
        protected Void doInBackground(Void... arg0) {
            String ruselut="";
            while (true) {
                try {
                    if(isCancelled())break;
                    if(currentTryTime>=PushConfig.MAX_RETRY_TIMES){
                        stopConnect();
                        break;
                    }
                    PushPreferences preferences=new PushPreferences(context,currentUserId);
                    currentTryTime++;

                    Log.i(PushConfig.LOG_TAG,"Connection try "+currentTryTime+" times");
                    if (push_token!=null&&!push_token.equals("")&&!isExpired()) {
                        connectToPushServer(preferences.getTempHost(),preferences.getTempPort());
                        break;
                    } else {
                        ruselut=getPushToken();
                        if (ruselut==null) {
                            Thread.sleep(PushConfig.RECONNECT_INTERVAL);
                        } else {
                            JSONObject object=new JSONObject(ruselut);
                            if (object.getBoolean("result")) {
                                String tempHost=object.getString("host");
                                int tempPort=object.getInt("port");
                                push_token=object.getString("push_Token");
                                tokenCreateDate=object.getString("expired");
                                expiredTime=object.getInt("expired_day");
                                preferences.saveExpiredTime(expiredTime);
                                preferences.saveToken(push_token);
                                preferences.saveTokenCreateTime(tokenCreateDate);
                                preferences.saveTempHost(tempHost);
                                preferences.saveTempPort(tempPort);
                                connectToPushServer(tempHost, tempPort);
                                break;
                            }
                        }
                    }

                } catch (Exception e) {
                    try {
                        Thread.sleep(PushConfig.RECONNECT_INTERVAL);
                    } catch (InterruptedException e1) {

                    }
                }
            }
            Log.i(PushConfig.LOG_TAG, "Conncet thread is ended");
            return null;
        }

    }
    public void stopConnect(){
        if(pushSocket!=null) pushSocket.disConnect();
        connectTask.cancel(true);
        unregisterHeartBeatReceiver();

        pushSocket=null;
        Log.i(PushConfig.LOG_TAG, "Push Manager Connect is Stop");
    }
    public void reConnect(boolean isSetTryTimeToZero){
        Log.i(PushConfig.LOG_TAG, "Reconnect.......");
        stopConnect();
        HeartbeatReciverList.clear();
        connectTask=null;
        connectTask=new ConnectTask();
        connect();
    }

    private void connectToPushServer(final String host,final int port){
        Log.i(PushConfig.LOG_TAG, "Start connect socket...");
        if(pushSocket!=null&&pushSocket.isSocketAvailable()){
            Log.i(PushConfig.LOG_TAG, "Socket connect is existed");
            return;
        }

        isSocketAuth=false;
        new Thread(new Runnable() {
            @Override
            public void run() {
                if(pushSocket==null)pushSocket=new PushSocket(host,port,self);
                pushSocket.disConnect();
                Log.i(PushConfig.LOG_TAG, "Connect to socket server");
                //如果连接成功，会阻塞在这里
                pushSocket.connectInternal();

                Log.i(PushConfig.LOG_TAG, "Socket connect is close");
            }
        }).start();
    }
    /**
     * 获得推送服务器对分配的Token
     * @return
     */
    private String getPushToken(){
        String url="http://"+server_host+":"+server_port+"/authenticate";
        List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();
        params.add(new BasicNameValuePair("appId",appId));
        params.add(new BasicNameValuePair("username",currentUserId));
        params.add(new BasicNameValuePair("deviceId",unique));
        params.add(new BasicNameValuePair("secret",secret));
        Log.i(PushConfig.LOG_TAG, "Try to get Push Token By ---"+currentUserId) ;
        return HttpConnection.post(url,params, context);
    }

    /**
     * 拉取推送消息
     */
    public String getPushMessage(String msgId,String host ,int port){
        Date d=new Date();
//        http://ip:port/v1/message/{username}/{deviceId}/{pushToken}?msgId=xxx&timestamp=xxx
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String url="http://"+host+":"+port+"/v1/message/"+currentUserId+
                "/"+unique+"/"+push_token+"?msgId="+msgId+"&timestamp="+format.format(d);
        String r=HttpConnection.get(url,context);
        return r;
    }

    /**
     * 拉取多条离线消息
     * @param host
     * @param port
     */
    public String getOfflineMessage(String host,int port){
//       http://ip:port/v1/messages/{username}/{deviceId}/{pushToken}?timestamp=xxxxx
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String url="http://"+host+":"+port+"/v1/messages/"
                +currentUserId+"/"+unique+"/"+push_token+"?timestamp="+format.format(new Date());
        String r=HttpConnection.get(url,context);
        return r;
    }
    /**
     * 消息回执
     * @param msgId
     */
    public void messageFeedback(String msgId,String host,int port){
        Date d=new Date();
//        http://ip:port/v1/message/{username}/{deviceId}/{pushToken}/{msgId}/{timestamp}
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String url="http://"+host+":"+port+"/v1/message/"+currentUserId+
                "/"+unique+"/"+push_token+"/"+msgId+"/"+format.format(d);
        String r=HttpConnection.put(url,context);
        return;
    }
    public void close() {
        stopConnect();
        if(pushSocket!=null)pushSocket.disConnect();
        push_token=null;
        try {
            context.unregisterReceiver(receiver);
        } catch (Exception e) {
        }
        unregisterHeartBeatReceiver();
        Log.i(PushConfig.LOG_TAG, "Push Manager is Close");
    }

    private void authToSocket(){

        if (pushSocket!=null&&pushSocket.isSocketAvailable()) {
            AuthMessageEntity entity=new AuthMessageEntity();
			entity.setDeviceId(unique);
            entity.setDeivceName(Build.MODEL);
            entity.setOsVersion(Build.VERSION.RELEASE);
            entity.setPushToken(push_token);
            entity.setUserName(currentUserId);
            entity.setAppName(context.getPackageName());
            entity.setTags(tagses);
            try {
                byte[] msg=PackageUtil.ToByte(entity);
                Log.i(PushConfig.LOG_TAG, "Auth To Socket Server");
                pushSocket.sendMsg(msg);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            connect();
        }
    }
    public void setTryTimesToZero(){
        currentTryTime=0;
    }
    /**
     * 已经在Socket服务器完成对Token 的验证
     */
    public void socketAuthed(){
        Log.i(PushConfig.LOG_TAG, "Socket auth is success");
        isSocketAuth=true;
    }
    /**
     * 对Token 的验证失败，需要获得新的Token
     */
    public void socketAuthError(){
        Log.i(PushConfig.LOG_TAG, "Socket auth is fail");
        push_token=null;
    }
    public void sendHeartBeat(){

        if (isSocketAuth) {//心跳
            if(HeartbeatReciverList.isOffline()){
                Log.i(PushConfig.LOG_TAG, "Socket is Offline");
                reConnect(true);
                return;
            }
            if (pushSocket!=null&&pushSocket.isSocketAvailable()) {
                Log.i(PushConfig.LOG_TAG,"Send Heartbeat");
                HeartbeatReciverList.insert(new TimeEntity());
                try {
                    byte[] msgHeartbeat=PackageUtil.ToByteNoJson("");
                    pushSocket.sendMsg(msgHeartbeat);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } else {
                connect();
            }
        } else {//如果认证还没成功，心跳的时候就到Socket服务器去进行认证
            authToSocket();
        }

    }

    public void registerHeartBeatReceiver(){

        Log.i(PushConfig.LOG_TAG, "Register Heartbeat Receiver");
        Intent intent = new Intent(context, HeartBeatReceiver.class);
        intent.setAction(PushConstants.HEARTBEAT_RECEIVER);
        PendingIntent pi=PendingIntent.getBroadcast(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

        am.setRepeating(AlarmManager.RTC_WAKEUP,SystemClock.elapsedRealtime(),PushConfig.HEARTBEAT_CYCLE,pi);
    }
    private void unregisterHeartBeatReceiver(){
        Log.i(PushConfig.LOG_TAG, "Unregister Heartbeat Receiver");
        Intent intent = new Intent(context, HeartBeatReceiver.class);
        intent.setAction("heartbeat.receiver");
        PendingIntent pi=PendingIntent.getBroadcast(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        am.cancel(pi);
    }

    /**
     * 判断PushToken是否过期
     * @return
     */
    private boolean isExpired(){
        try {
            DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault());
            Date d=format.parse(tokenCreateDate);
            Calendar cNow=Calendar.getInstance();
            Calendar cD=Calendar.getInstance();
            cD.setTime(d);
            cD.add(Calendar.DAY_OF_MONTH,expiredTime);
            int c=cD.compareTo(cNow);
            if (c>0) {
                return false;
            } else {
                return true;
            }
        } catch (ParseException e) {
            return true;
        }
    }


}
